package com.rankeiot.core;

import com.rankeiot.core.anno.Menu;
import com.rankeiot.core.anno.Permission;
import com.rankeiot.core.data.Item;
import com.rankeiot.core.data.MenuItem;
import com.rankeiot.core.dict.UserType;
import com.rankeiot.core.util.LocaleUtil;
import com.rankeiot.core.util.StringUtil;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 菜单管理
 */
public class MenuManager {

    //全部用户菜单
//    private static  Map<String, MenuItem> allMenus=new ConcurrentHashMap<>();
//    //全部会员菜单
//    private static  Map<String,MenuItem> memberMenus=new ConcurrentHashMap<>();

    private static Map<String,Map<String,MenuItem>> groupMenus=new ConcurrentHashMap<>();


    public static Map<String,Map<String,MenuItem>> getGroupMenus(){
        return groupMenus;
    }
    public static MenuItem getMenuItem(String group,String id){
        Map<String,MenuItem> groups= getMenus(group);
        if(groups==null){
            return null;
        }
        return groups.get(id);
    }
    public static synchronized Map<String,MenuItem> getMenus(String group){
        Map<String,MenuItem> menus = groupMenus.get(group);
        if(menus==null){
            menus=new ConcurrentHashMap<>();
            groupMenus.put(group,menus);
        }
        return menus;
    }
    public static void addMemberMenu(MenuItem item){
        addMenu0(item, getMenus(UserType.M.getValue()));
    }
    public static void addMenu(MenuItem item){
        addMenu0(item, getMenus(UserType.U.getValue()));
    }
    public static void addMenu(String group,MenuItem item){
        Map<String, MenuItem> menus = getMenus(group);
        addMenu0(item,menus);
    }
    private static void addMenu0(MenuItem item, Map<String,MenuItem> allMenus){
        if(StringUtil.isNotEmpty(item.getParent())){
            MenuItem parent=allMenus.get(item.getParent());
            if(parent==null){
                parent=new MenuItem(item.getParent(),item.getParent());
                parent.setChildren(new ArrayList<>());
                parent.getChildren().add(item);
                allMenus.put(parent.getKey(),parent);
            }else{
                if(parent.getChildren()==null){
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(item);
            }
        }
        if(allMenus.containsKey(item.getKey())){
            MenuItem old= allMenus.get(item.getKey());
            old.setIcon(item.getIcon());
            old.setOrder(item.getOrder());
            old.setParent(item.getParent());
            old.setPath(item.getPath());
            old.setTitle(item.getTitle());
        }else {
            allMenus.put(item.getKey(),item);
        }
    }

    protected  static void sortMenus(Map<String,MenuItem> allMenus){
        for(MenuItem item:allMenus.values()){
            if(item.getChildren()!=null){
                Collections.sort(item.getChildren(), Comparator.comparingInt(Item::getOrder));
            }
        }
    }
    public static void regMenu(Class c) {
        regMenu0(c,MenuManager::addMenu);
    }
    public static void regMemberMenu(Class c) {
        regMenu0(c,MenuManager::addMemberMenu);
    }
    private static void regMenu0(Class c, Consumer<MenuItem> add) {
        if (!c.isInterface()) {
            return;
        }
        String parent="";
        Menu moduleMenu= (Menu) c.getDeclaredAnnotation(Menu.class);
        if(moduleMenu==null|| "".equals(moduleMenu.id())){
            try {
               Field f= c.getField("ID");
               parent= (String) f.get(c);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                parent=c.getName().toLowerCase().replaceFirst("menu$","");
            }
        }else{
            parent=moduleMenu.id();
        }
        if(moduleMenu!=null){

            MenuItem menuItem = new MenuItem(parent, LocaleUtil.encode(moduleMenu.value(),moduleMenu.en()), moduleMenu.icon(), moduleMenu.order());
            menuItem.setPath(moduleMenu.path());
            if(!"\0".equals(moduleMenu.parent())){
                menuItem.setParent(moduleMenu.parent());
            }
            add.accept(menuItem);
        }
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            Menu menu = field.getAnnotation(Menu.class);
            String v = null;
            try {
                v = field.get(c).toString();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if (menu != null) {
                MenuItem menuItem = new MenuItem(v,LocaleUtil.encode( menu.value(),menu.en()), menu.icon(), menu.order());
                menuItem.setPath(menu.path());
                if("\0".equals(menu.parent())){
                    menuItem.setParent(parent);
                }else if(StringUtil.isEmpty(menu.parent())){
                    menuItem.setParent(null);
                }else{
                    menuItem.setParent(menu.parent());
                }
                add.accept(menuItem);
            } else {
                Permission permission = field.getAnnotation(Permission.class);
                if(permission==null){
                    continue;
                }
                MenuItem menuItem = new MenuItem(v,LocaleUtil.encode(permission.value(),permission.en()));
                menuItem.setOrder(permission.order());
                menuItem.setType(MenuItem.TYPE_PERMISSION);
                menuItem.setParent(permission.parent());
                add.accept(menuItem);
            }
        }
    }

    /**
     * 全部菜单权限
     * @return
     */
    public static List<MenuItem> allPermission(){
        List<MenuItem> menus=new ArrayList<>();
        getMenus(UserType.U.getValue()).forEach((s, item) -> {
            if(item.getParent()==null){
                menus.add(item);
            }
        });
        menus.sort(Comparator.comparingInt(Item::getOrder));
        return menus;
    }
    public static Set<String> allStrPermission(){
       return getMenus(UserType.U.getValue()).keySet();
    }
    public static List<? extends Item> getAllMemberMenu(){
        List<MenuItem> menus =getMenus(UserType.M.getValue())
                .values().stream()
                .filter(it->it.isEnable())
                .collect(Collectors.toList());
        return toTree(menus);
    }
    public static List<MenuItem>  getAllMenu(Set<String> permissions,boolean isAdmin){
        List<MenuItem> menus=new ArrayList<>();
        Locale locale = LocaleUtil.getLocale();
        getMenus(UserType.U.getValue()).forEach((s, item) -> {
            if(item.getType()==MenuItem.TYPE_PERMISSION){
                return;
            }
            if(!item.isEnable()){
                return;
            }
            if(isAdmin){
                MenuItem menuItem=new MenuItem(item);
                menuItem.setTitle(LocaleUtil.d(menuItem.getTitle(),locale));
                menus.add(menuItem);
            }else if(hasAnyPermission(permissions,item)){
                MenuItem menuItem=new MenuItem(item);
                menuItem.setTitle(LocaleUtil.d(menuItem.getTitle(),locale));
                menus.add(menuItem);
            }
        });
        return toTree(menus);
    }
    private static boolean hasAnyPermission(Set<String> permissions,MenuItem menu){
        if(permissions.contains(menu.getKey())){
            return true;
        }
        return false;
    }
    private static List<MenuItem> toTree(List<MenuItem> menus){
        List<MenuItem> result=new ArrayList<>();
        Map<String,MenuItem> items=new HashMap<>();
        for(MenuItem item:menus){
            if(item.getParent()==null){
                result.add(item);
            }
            items.put(item.getKey(),item);
        }
        for(MenuItem item:menus){
            if(item.getParent()!=null){
                MenuItem parent=items.get(item.getParent());
                if(parent!=null){
                    if(parent.getChildren()==null){
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(item);
                }
            }
        }
        result.sort(Comparator.comparingInt(MenuItem::getOrder));
        items.forEach((s,item)->{
            if(item.getChildren()!=null){
                item.getChildren().sort(Comparator.comparingInt(Item::getOrder));
            }
        });
        return result;
    }

//    private static Set<String> excludedMenu = new HashSet<>();
//
//    public static void addExcludeMenu(String id){
//        excludedMenu.add(id);
//    }
}
